﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "SerialPort.h"
#include <functional>
#include <QApplication>
#include "../platform/Securec/secinput.h"
#include "DataConvertor.h"

using namespace std;
using namespace SerialPortConstantValue;
namespace {
    const int STOPBITS_VALUE_TWO = 2;
    const int STOPBITS_VALUE_THREE = 3;
    const int DATABITS_VALUE_FIVE = 5;
    const int DATABITS_VALUE_SIX = 6;
    const int DATABITS_VALUE_SEVEN = 7;
    const int DATABITS_VALUE_EIGHT = 8;
    const int SLEEPTIME_100 = 100;
}

SerialPort::SerialPort(QObject *parent)
    : PortBase(parent),
      serialPort(nullptr),
      readThread(nullptr),
      serialPortWork(nullptr),
      witeThreadMsgId(0),
      witeThread(nullptr),
      oneTimeReadByte(0),
      isForceReadWithSleep(TRUE),
      forceReadTime(10), // 10ms
      owner(nullptr),
      portNr(0),
      writeBuffer(nullptr),
      writeSize(1),
      alreadyReadByte(0),
      writeBufferSize(0),
      isThreadAlive(false),
      msgId(WM_COMM_RXCHAR),
      isPortOpen(false)
{
    communicationSync.SpinCount = 0;
    communicationSync.LockSemaphore = nullptr;
    communicationSync.DebugInfo = nullptr;
    communicationSync.LockCount = 0;
    communicationSync.RecursionCount = 0;
    communicationSync.OwningThread = nullptr;
    for (int i = 0; i < 0x2800; i++) { // 0x2800 数组大小
        rxBuffer[i] = 0;
    }
    serialPortWork = new Work::SerialPortWork();
    readThread = new MyThreadClass::MyThread();
    if (serialPortWork != nullptr) {
        serialPortWork->moveToThread(readThread);
        connect(readThread, &QThread::finished, serialPortWork, &QObject::deleteLater);
        connect(serialPortWork, &Work::SerialPortWork::SignalEventProcess, this,
                &SerialPort::SlotReadyRead, Qt::DirectConnection);
    }
    if (readThread != nullptr) {
        readThread->start(QThread::TimeCriticalPriority);
    }
}

SerialPort::~SerialPort()
{
    SerialPort::ClosePort();
    if (serialPort != nullptr) {
        delete serialPort;
        serialPort = nullptr;
    }
    if (serialPortWork != nullptr) {
        delete serialPortWork;
        serialPortWork = nullptr;
    }
    if (readThread != nullptr) {
        readThread->quit();
        readThread->wait();
        delete readThread;
        readThread = nullptr;
    }
    if (writeBuffer != nullptr) {
        delete[] writeBuffer;
        writeBuffer = nullptr;
    }
}

bool SerialPort::InitPort(PortBaseInitStru &serialPortInit)
{
    if (isThreadAlive && readThread != nullptr) {
        readThread->quit();
        readThread->wait();
    }
    InitPortClean();
    InitializeCriticalSection(&communicationSync);

    if (writeBuffer != nullptr) {
        delete[] writeBuffer;
        writeBuffer = nullptr;
    }
    if (serialPortInit.writebuffersize == 0) {
        return false;
    }
    writeBuffer = new char[serialPortInit.writebuffersize];
    InitFiled(serialPortInit);
    if (serialPort == nullptr) {
        serialPort = new QSerialPort();
        if (serialPort != nullptr) {
            qRegisterMetaType<QSerialPort::SerialPortError>("QSerialPort::SerialPortError");
            connect(serialPort, static_cast<void (QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
                    this, &SerialPort::SlotHandleSerialError);
            connect(serialPort, &QIODevice::readyRead, serialPortWork, &Work::SerialPortWork::CommThread);
        }
    }
    if (serialPort != nullptr) {
        serialPort->close();
        serialPort->setParity(serialPortInit.parity);
        SetFlowCtrl(serialPortInit.flowctrl);
        serialPort->setBaudRate(serialPortInit.baud);
        SetStopBits(serialPortInit.stopbits);
        SetDataBits(serialPortInit.databits);
        serialPort->setPortName("COM" + QString::number(serialPortInit.portnr));
        isPortOpen = serialPort->open(QSerialPort::ReadWrite);
        serialPort->setDataTerminalReady(false);
    }
    if (!isPortOpen) {
        return false;
    }
    return true;
}

bool SerialPort::SetFlowCtrl(UINT flowctrl)
{
    if (serialPort == nullptr) {
        return false;
    }
    bool ret = false;
    if (flowctrl == 0) {
        ret = serialPort->setFlowControl(QSerialPort::NoFlowControl);
    } else if (flowctrl == 1) {
        ret = serialPort->setFlowControl(QSerialPort::HardwareControl);
    }
    return ret;
}

bool SerialPort::SetStopBits(UINT stopBits)
{
    if (serialPort == nullptr) {
        return false;
    }
    bool ret = false;
    if (stopBits == 1) {
        ret = serialPort->setStopBits(QSerialPort::OneStop);
    } else if (stopBits == STOPBITS_VALUE_TWO) {
        ret = serialPort->setStopBits(QSerialPort::TwoStop);
    } else if (stopBits == STOPBITS_VALUE_THREE) {
        ret = serialPort->setStopBits(QSerialPort::OneAndHalfStop);
    } else {
        ret = serialPort->setStopBits(QSerialPort::UnknownStopBits);
    }
    return ret;
}

bool SerialPort::SetDataBits(UINT dataBits)
{
    bool ret = false;
    if (dataBits == DATABITS_VALUE_FIVE) {
        ret = serialPort->setDataBits(QSerialPort::Data5);
    } else if (dataBits == DATABITS_VALUE_SIX) {
        ret = serialPort->setDataBits(QSerialPort::Data6);
    } else if (dataBits == DATABITS_VALUE_SEVEN) {
        ret = serialPort->setDataBits(QSerialPort::Data7);
    } else if (dataBits == DATABITS_VALUE_EIGHT) {
        ret = serialPort->setDataBits(QSerialPort::Data8);
    } else {
        ret = serialPort->setDataBits(QSerialPort::UnknownDataBits);
    }
    return ret;
}

void SerialPort::InitFiled(const PortBaseInitStru &serialPortInit)
{
    owner = serialPortInit.pPortOwner;
    witeThreadMsgId = serialPortInit.witeThreadMsgId;
    witeThread = serialPortInit.witeThread;
    oneTimeReadByte = serialPortInit.oneTimeReadByte;
    portNr = serialPortInit.portnr;
    writeBufferSize = serialPortInit.writebuffersize;
    isForceReadWithSleep = serialPortInit.isForceReadWithSleep;
    forceReadTime = serialPortInit.forceReadTime;
}

ErrorSettingCom SerialPort::InitPortConfigure(UINT stopbits, UINT flowctrl, UINT baud,
                                              QSerialPort::Parity parity, UINT databits)
{
    if (serialPort == nullptr) {
        return ErrorSettingCom::ERROR_SETTING_COM_SERIAL_PORT_NULL;
    }

    if (!serialPort->setBaudRate(baud)) {
        return ErrorSettingCom::ERROR_SETTING_COM_BAUDRATE;
    }
    if (!serialPort->setParity(parity)) {
        return ErrorSettingCom::ERROR_SETTING_COM_PARITY;
    }
    if (!SetFlowCtrl(flowctrl)) {
        return ErrorSettingCom::ERROR_SETTING_COM_FLOW_CTRL;
    }
    if (!SetStopBits(stopbits)) {
        return ErrorSettingCom::ERROR_SETTING_COM_STOPBITS;
    }
    if (!SetDataBits(databits)) {
        return ErrorSettingCom::ERROR_SETTING_COM_DATABITS;
    }
    return ErrorSettingCom::ERROR_SETTING_COM_SUCCESS;
}

void SerialPort::InitPortClean()
{
    SecureZeroMemory(rxBuffer, sizeof(rxBuffer));
    SecureZeroMemory(&communicationSync, sizeof(CRITICAL_SECTION));
}

BOOL SerialPort::ReInitPort()
{
    if (serialPort != nullptr) {
        Sleep(SLEEPTIME_100);  // 100ms
        delete serialPort;
        serialPort = nullptr;
        Sleep(SLEEPTIME_100);  // 100ms
    }
    serialPort = new QSerialPort();
    if (serialPort == nullptr) {
        return FALSE;
    }
    qRegisterMetaType<QSerialPort::SerialPortError>("QSerialPort::SerialPortError");
    connect(serialPort, static_cast<void (QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
            this, &SerialPort::SlotHandleSerialError);
    connect(serialPort, &QIODevice::readyRead, serialPortWork, &Work::SerialPortWork::CommThread);
    serialPort->clear();
    serialPort->setPortName("COM" + QString::number(portNr));
    isPortOpen = serialPort->open(QSerialPort::ReadWrite);
    serialPort->setDataTerminalReady(false);
    return TRUE;
}

BOOL SerialPort::StartMonitoring()
{
    if (serialPortWork == nullptr) {
        serialPortWork = new Work::SerialPortWork(this);
    }

    isThreadAlive = true;
    if (readThread == nullptr) {
        return false;
    }
    return true;
}

void SerialPort::WriteChar()
{
    if (!isThreadAlive) {
        return;
    }

    EnterCriticalSection(&communicationSync);
    if (isPortOpen) {
        QMetaObject::invokeMethod(witeThread,
                                  std::bind(static_cast<qint64(QSerialPort::*)(const CHAR *,
                                                                               qint64)>(&QSerialPort::write),
                                            serialPort, writeBuffer, writeSize));
    }
    LeaveCriticalSection(&communicationSync);
}

void SerialPort::ReportBuffer(SerialPort &port)
{
    int txBuffSize = port.alreadyReadByte;
    if (port.alreadyReadByte > RX_BUFFER_SIZE) {
        txBuffSize = RX_BUFFER_SIZE;
    }
    auto txBuff = new unsigned char[txBuffSize];
    if (txBuff != nullptr) {
        if (memcpy_s(txBuff, txBuffSize, port.rxBuffer, txBuffSize) != 0) {
            return;
        }
        SecureZeroMemory(port.rxBuffer, port.alreadyReadByte);
        if (port.owner != nullptr) {
            emit port.SignalSendMsg(port.msgId, reinterpret_cast<WPARAM>(txBuff),
                                    static_cast<LPARAM>(port.alreadyReadByte));
        } else {
            emit port.SignalSendMsg(port.witeThreadMsgId, reinterpret_cast<WPARAM>(txBuff),
                                    static_cast<LPARAM>(port.alreadyReadByte));
        }
    }
}

void SerialPort::WriteToPort(const char *string, int n)
{
    if (writeBuffer == nullptr || string == nullptr || memcpy_s(writeBuffer, writeBufferSize, string, n) != 0) {
        return;
    }
    writeSize = n;
    WriteChar();
}

bool SerialPort::IsThreadAlive() const
{
    return isThreadAlive;
}

void SerialPort::SetMsgId(UINT nMsgId)
{
    this->msgId = nMsgId;
}

void SerialPort::SetOneTimeReadByte(DWORD mOneTimeReadByte)
{
    this->oneTimeReadByte = mOneTimeReadByte;
}

void SerialPort::ClosePort()
{
    if (serialPort != nullptr && serialPort->isOpen()) {
        serialPort->close();
    }

    isThreadAlive = false;
    isPortOpen = false;
}

void SerialPort::SlotHandleSerialError(QSerialPort::SerialPortError error)
{
    if (error == QSerialPort::ResourceError) {
        ClosePort();
    }
}

void SerialPort::SlotReadyRead()
{
    if (!isThreadAlive) {
        return;
    }

    if (sizeof(rxBuffer) - alreadyReadByte == 0) {
        return;
    }

    if (serialPort == nullptr) {
        return;
    }
    uint64_t avaiableLen = serialPort->bytesAvailable();
    if (avaiableLen > sizeof (rxBuffer) - alreadyReadByte) {
        avaiableLen = sizeof (rxBuffer) - alreadyReadByte;
    }
    int bResult = 0;
    bResult = serialPort->read(rxBuffer + alreadyReadByte, avaiableLen);
    if (bResult == -1) {
    } else if (bResult != 0) {
        alreadyReadByte += bResult;
    } else {
        return;
    }

    if (alreadyReadByte <= oneTimeReadByte) {
        return;
    }
    if (alreadyReadByte <= 0) {
        return;
    }
    ReportBuffer(*this);
    alreadyReadByte = 0;
}
